package crypto

import (
	"bytes"
	"crypto/ecdsa"
	"crypto/elliptic"
	cr "crypto/rand"
	"crypto/rsa"
	"crypto/tls"
	"crypto/x509"
	"crypto/x509/pkix"
	"encoding/pem"
	"io"
	"math/big"
	"math/rand"
	"net"
	"time"

	"github.com/beherochuling/beesoft_api/app/service/util"
)

type cert struct {
	PrivateKey  *rsa.PrivateKey
	Certificate *x509.Certificate
	Cert        []byte
	CertPem     *bytes.Buffer
	CertKeyPem  *bytes.Buffer
}

func NewCert() *cert {
	return new(cert)
}

func (r *cert) generateKey() *rsa.PrivateKey {
	privateKey, err := rsa.GenerateKey(cr.Reader, 4096)
	if err != nil {
		panic(err)
	}

	return privateKey
}
func (r *cert) generateKey2() *ecdsa.PrivateKey {
	privateKey, err := ecdsa.GenerateKey(elliptic.P256(), cr.Reader)
	if err != nil {
		panic(err)
	}

	return privateKey
}

func (r *cert) createCertificate(certificate, certificateAuthor *x509.Certificate, privateKey *rsa.PrivateKey) []byte {
	cert, err := x509.CreateCertificate(cr.Reader, certificate, certificateAuthor, &privateKey.PublicKey, privateKey)
	if err != nil {
		panic(err)
	}

	// certificate, err := x509.ParseCertificate(cert) // todo
	// if err != nil {
	// 	panic(err)
	// }

	return cert
}

func (r *cert) convert(writer io.Writer, certType string, certBytes []byte) {
	err := pem.Encode(writer, &pem.Block{
		Type:  certType,
		Bytes: certBytes,
	})
	if err != nil {
		panic(err)
	}
}

func (r *cert) CreateCA(sub *pkix.Name, expire int) *cert {
	r.PrivateKey = r.generateKey()
	r.Certificate = &x509.Certificate{
		SerialNumber:          big.NewInt(rand.Int63n(2000)),
		Subject:               *sub,
		NotBefore:             time.Now(),                       // 生效时间
		NotAfter:              time.Now().AddDate(expire, 0, 0), // 过期时间
		KeyUsage:              x509.KeyUsageDigitalSignature | x509.KeyUsageCertSign,
		ExtKeyUsage:           []x509.ExtKeyUsage{x509.ExtKeyUsageClientAuth, x509.ExtKeyUsageServerAuth},
		BasicConstraintsValid: true, // 基本约束
		IsCA:                  true, // CA
	}
	r.Cert = r.createCertificate(r.Certificate, r.Certificate, r.PrivateKey)
	r.CertPem = new(bytes.Buffer)
	r.convert(r.CertPem, "CERTIFICATE", r.Cert)
	r.CertKeyPem = new(bytes.Buffer)
	r.convert(r.CertKeyPem, "RSA PRIVATE KEY", x509.MarshalPKCS1PrivateKey(r.PrivateKey))

	return r
}
func (r *cert) CreateCert(ca *x509.Certificate, sub *pkix.Name, expire int, dns []string, ip []net.IP) *cert {
	r.PrivateKey = r.generateKey()
	r.Certificate = &x509.Certificate{
		SerialNumber: big.NewInt(rand.Int63n(2000)),
		Subject:      *sub,
		NotBefore:    time.Now(),
		NotAfter:     time.Now().AddDate(expire, 0, 0),
		KeyUsage:     x509.KeyUsageDigitalSignature,
		ExtKeyUsage:  []x509.ExtKeyUsage{x509.ExtKeyUsageClientAuth, x509.ExtKeyUsageServerAuth},
		SubjectKeyId: []byte{1, 2, 3, 4, 6},
		IPAddresses:  ip,
		DNSNames:     dns,
	}
	r.Cert = r.createCertificate(r.Certificate, ca, r.PrivateKey)
	r.CertPem = new(bytes.Buffer)
	r.convert(r.CertPem, "CERTIFICATE", r.Cert)
	r.CertKeyPem = new(bytes.Buffer)
	r.convert(r.CertKeyPem, "RSA PRIVATE KEY", x509.MarshalPKCS1PrivateKey(r.PrivateKey))

	return r
}

func (r *cert) Export(path string) {
	util.WriteFile(path+".key", r.CertKeyPem.Bytes())
	util.WriteFile(path+".crt", r.CertPem.Bytes())
}
func (r *cert) Import(cert, key string) *tls.Certificate { // todo
	tlsCert, err := tls.LoadX509KeyPair(cert, key)
	if err != nil {
		panic(err)
	}

	return &tlsCert
}
